home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / jade / src / buffers.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  30KB  |  1,147 lines

  1. /* buffers.c -- Buffer handling
  2.    Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
  3.  
  4.    This file is part of Jade.
  5.  
  6.    Jade is free software; you can redistribute it and/or modify it
  7.    under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2, or (at your option)
  9.    any later version.
  10.  
  11.    Jade is distributed in the hope that it will be useful, but
  12.    WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with Jade; see the file COPYING.    If not, write to
  18.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "jade.h"
  21. #include "jade_protos.h"
  22.  
  23. #include <string.h>
  24. #include <stdlib.h>
  25.  
  26. _PR void buffer_sweep(void);
  27. _PR void buffer_prin(VALUE, VALUE);
  28. _PR TX *first_buffer(void);
  29. _PR void swap_buffers(VW *, TX *);
  30. _PR TX *swap_buffers_tmp(VW *, TX *);
  31. _PR POS *get_tx_cursor(TX *);
  32. _PR int auto_save_buffers(void);
  33. _PR void make_marks_resident(TX *);
  34. _PR void make_marks_non_resident(TX *);
  35. _PR void mark_sweep(void);
  36. _PR int mark_cmp(VALUE, VALUE);
  37. _PR void mark_prin(VALUE, VALUE);
  38. _PR void buffers_init(void);
  39. _PR void buffers_kill(void);
  40.  
  41. /*
  42.  * Chain of all allocated TXs.
  43.  */
  44. _PR TX *buffer_chain;
  45. TX *buffer_chain;
  46.  
  47. VALUE sym_auto_save_function, sym_leading, sym_all;
  48.  
  49. _PR VALUE cmd_make_buffer_name(VALUE);
  50. DEFUN("make-buffer-name", cmd_make_buffer_name, subr_make_buffer_name, (VALUE rawName), V_Subr1, DOC_make_buffer_name) /*
  51. ::doc:make_buffer_name::
  52. make-buffer-name NAME
  53.  
  54. Construct a unique buffer-name from NAME.
  55. ::end:: */
  56. {
  57.     int suffix = 1;
  58.     DECLARE1(rawName, STRINGP);
  59.     while(TRUE)
  60.     {
  61.     u_char buf[256];
  62.     u_char *thistry;
  63.     TX *tx = buffer_chain;
  64.     if(suffix != 1)
  65.     {
  66.         sprintf(buf, "%s<%d>", VSTR(rawName), suffix);
  67.         thistry = buf;
  68.     }
  69.     else
  70.         thistry = VSTR(rawName);
  71.     while(tx)
  72.     {
  73.         if(tx->tx_BufferName && !strcmp(thistry, VSTR(tx->tx_BufferName)))
  74.         break;
  75.         tx = tx->tx_Next;
  76.     }
  77.     if(!tx)
  78.     {
  79.         if(suffix == 1)
  80.         return(rawName);
  81.         return(string_dup(buf));
  82.     }
  83.     suffix++;
  84.     }
  85. }
  86.  
  87. _PR VALUE cmd_make_buffer(VALUE, VALUE, VALUE);
  88. DEFUN("make-buffer", cmd_make_buffer, subr_make_buffer, (VALUE name, VALUE oldTx, VALUE litName), V_Subr3, DOC_make_buffer) /*
  89. ::doc:make_buffer::
  90. make-buffer NAME
  91.  
  92. Return a new buffer, it's name is the result of (make-buffer-name NAME).
  93. ::end:: */
  94. {
  95.     TX *tx;
  96.     DECLARE1(name, STRINGP);
  97.     if(!BUFFERP(oldTx))
  98.     {
  99.     if(curr_vw)
  100.         oldTx = VAL(curr_vw->vw_Tx);
  101.     else
  102.         oldTx = NULL;
  103.     }
  104.     if((tx = mycalloc(sizeof(TX))) && clear_line_list(tx))
  105.     {
  106.     tx->tx_Type = V_Buffer;
  107.     tx->tx_Next = buffer_chain;
  108.     buffer_chain = tx;
  109.     data_after_gc += sizeof(TX);
  110.     tx->tx_BufferName = NILP(litName) ? cmd_make_buffer_name(name) : name;
  111.     if(tx->tx_BufferName)
  112.     {
  113.         tx->tx_FileName = null_string;
  114.         tx->tx_MinorModeNameList = sym_nil;
  115.         tx->tx_MinorModeNameString = null_string;
  116.         tx->tx_SavedBlockStatus = -1;
  117.         tx->tx_TabSize = 8;
  118.         tx->tx_LocalVariables = sym_nil;
  119.         tx->tx_GlyphTable = oldTx ? VTX(oldTx)->tx_GlyphTable
  120.                               : cmd_default_glyph_table();
  121.         tx->tx_LastSaveTime = sys_time();
  122.         tx->tx_UndoList = sym_nil;
  123.         tx->tx_ToUndoList = NULL;
  124.         tx->tx_UndoneList = sym_nil;
  125.         return(VAL(tx));
  126.     }
  127.     }
  128.     return(NULL);
  129. }
  130.  
  131. _PR VALUE cmd_destroy_buffer(VALUE);
  132. DEFUN("destroy-buffer", cmd_destroy_buffer, subr_destroy_buffer, (VALUE tx), V_Subr1, DOC_destroy_buffer) /*
  133. ::doc:destroy_buffer::
  134. destory-buffer BUFFER
  135.  
  136. Throw away everything associated with buffer. All resident marks are made
  137. non-resident.
  138. ::end:: */
  139. {
  140.     DECLARE1(tx, BUFFERP);
  141.     make_marks_non_resident(VTX(tx));
  142.     clear_line_list(VTX(tx));
  143.     VTX(tx)->tx_FileName = null_string;
  144.     VTX(tx)->tx_BufferName = null_string;
  145.     VTX(tx)->tx_ModeName = NULL;
  146.     VTX(tx)->tx_MinorModeNameList = sym_nil;
  147.     VTX(tx)->tx_MinorModeNameString = null_string;
  148.     VTX(tx)->tx_Changes = 0;
  149.     VTX(tx)->tx_LocalVariables = sym_nil;
  150.     VTX(tx)->tx_GlyphTable = cmd_default_glyph_table();
  151.     VTX(tx)->tx_Flags |= TXFF_RDONLY | TXFF_REFRESH_ALL | TXFF_NO_UNDO;
  152.     VTX(tx)->tx_UndoList = sym_nil;
  153.     VTX(tx)->tx_ToUndoList = NULL;
  154.     VTX(tx)->tx_UndoneList = sym_nil;
  155. #if 0
  156.     sm_flush(&main_strmem);
  157. #endif
  158.     return(sym_t);
  159. }
  160.  
  161. void
  162. buffer_sweep(void)
  163. {
  164.     TX *tx = buffer_chain;
  165.     buffer_chain = NULL;
  166.     while(tx)
  167.     {
  168.     TX *nxt = tx->tx_Next;
  169.     if(!GC_MARKEDP(VAL(tx)))
  170.     {
  171.         make_marks_non_resident(tx);
  172.         kill_line_list(tx);
  173.         myfree(tx);
  174.     }
  175.     else
  176.     {
  177.         GC_CLR(VAL(tx));
  178.         tx->tx_Next = buffer_chain;
  179.         buffer_chain = tx;
  180.     }
  181.     tx = nxt;
  182.     }
  183. }
  184. void
  185. buffer_prin(VALUE strm, VALUE obj)
  186. {
  187.     stream_puts(strm, "#<buffer ", -1, FALSE);
  188.     stream_puts(strm, VSTR(VTX(obj)->tx_BufferName), -1, TRUE);
  189.     stream_putc(strm, '>');
  190. }
  191.  
  192. TX *
  193. first_buffer(void)
  194. {
  195.     TX *tx;
  196.     if(!curr_vw)
  197.     {
  198.     curr_vw = VWIN(cmd_make_window(sym_nil, sym_nil, sym_nil, sym_nil));
  199.     if(!curr_vw)
  200.         return(NULL);
  201.     }
  202.     tx = VTX(cmd_make_buffer(MKSTR("*jade*"), sym_nil, sym_t));
  203.     if(tx)
  204.     {
  205.     swap_buffers(curr_vw, tx);
  206. #ifndef NOSCRLBAR
  207.     sys_update_scroller(curr_vw);
  208. #endif
  209.     return(tx);
  210.     }
  211.     return(NULL);
  212. }
  213.  
  214. /*
  215.  * Makes `new' the file being shown in window `vw'
  216.  */
  217. void
  218. swap_buffers(VW *vw, TX *new)
  219. {
  220.     TX *old = vw->vw_Tx;
  221.     if(old != new)
  222.     {
  223.     if(old)
  224.     {
  225.         old->tx_SavedCPos = vw->vw_CursorPos;
  226.         old->tx_SavedWPos = vw->vw_DisplayOrigin;
  227.         old->tx_SavedBlockPos[0] = vw->vw_BlockS;
  228.         old->tx_SavedBlockPos[1] = vw->vw_BlockE;
  229.         old->tx_SavedBlockStatus = vw->vw_BlockStatus;
  230.     }
  231.     vw->vw_Tx = new;
  232.     vw->vw_CursorPos = new->tx_SavedCPos;
  233.     vw->vw_DisplayOrigin = new->tx_SavedWPos;
  234.     vw->vw_BlockS = new->tx_SavedBlockPos[0];
  235.     vw->vw_BlockE = new->tx_SavedBlockPos[1];
  236.     vw->vw_BlockStatus = new->tx_SavedBlockStatus;
  237.     vw->vw_LastRefTx = NULL;
  238.     }
  239. }
  240. /*
  241.  * "nd" means non-destructive. refcount's of buffers are not changed and
  242.  * previous buffer shown is returned.
  243.  * This is intended to allow *temporary* switching of buffers before
  244.  * reinstalling the original.
  245.  * ** this is kind of obsolete but never mind **
  246.  */
  247. TX *
  248. swap_buffers_tmp(VW *vw, TX *new)
  249. {
  250.     TX *old = vw->vw_Tx;
  251.     if(old != new)
  252.     {
  253.     if(old)
  254.     {
  255.         old->tx_SavedCPos = vw->vw_CursorPos;
  256.         old->tx_SavedWPos = vw->vw_DisplayOrigin;
  257.         old->tx_SavedBlockPos[0] = vw->vw_BlockS;
  258.         old->tx_SavedBlockPos[1] = vw->vw_BlockE;
  259.         old->tx_SavedBlockStatus = vw->vw_BlockStatus;
  260.     }
  261.     vw->vw_Tx = new;
  262.     vw->vw_CursorPos = new->tx_SavedCPos;
  263.     vw->vw_DisplayOrigin = new->tx_SavedWPos;
  264.     vw->vw_BlockS = new->tx_SavedBlockPos[0];
  265.     vw->vw_BlockE = new->tx_SavedBlockPos[1];
  266.     vw->vw_BlockStatus = new->tx_SavedBlockStatus;
  267.     }
  268.     return(old);
  269. }
  270.  
  271. _PR VALUE cmd_get_file_buffer(VALUE);
  272. DEFUN("get-file-buffer", cmd_get_file_buffer, subr_get_file_buffer, (VALUE name), V_Subr1, DOC_get_file_buffer) /*
  273. ::doc:get_file_buffer::
  274. get-file-buffer NAME
  275.  
  276. Scan all buffers for one containing the file NAME.
  277. ::end:: */
  278. {
  279.     TX *tx = buffer_chain;
  280.     DECLARE1(name, STRINGP);
  281.     while(tx)
  282.     {
  283.     if(same_files(VSTR(name), VSTR(tx->tx_FileName)))
  284.         return(VAL(tx));
  285.     tx = tx->tx_Next;
  286.     }
  287.     return(sym_nil);
  288. }
  289.  
  290. _PR VALUE cmd_get_buffer(VALUE);
  291. DEFUN("get-buffer", cmd_get_buffer, subr_get_buffer, (VALUE name), V_Subr1, DOC_get_buffer) /*
  292. ::doc:get_buffer::
  293. get-buffer NAME
  294.  
  295. Scan all buffers for one whose name is NAME.
  296. ::end:: */
  297. {
  298.     TX *tx = buffer_chain;
  299.     if(BUFFERP(name))
  300.     return(name);
  301.     DECLARE1(name, STRINGP);
  302.     while(tx)
  303.     {
  304.     if(!strcmp(VSTR(name), VSTR(tx->tx_BufferName)))
  305.         return(VAL(tx));
  306.     tx = tx->tx_Next;
  307.     }
  308.     return(sym_nil);
  309. }
  310.  
  311. POS *
  312. get_tx_cursor(TX *tx)
  313. {
  314.     VW *vw = view_chain;
  315.     /* Check active window first */
  316.     if(curr_vw->vw_Tx == tx)
  317.     return(&curr_vw->vw_CursorPos);
  318.     while(vw)
  319.     {
  320.     if(vw->vw_Window && (vw->vw_Tx == tx))
  321.         return(&vw->vw_CursorPos);
  322.     vw = vw->vw_Next;
  323.     }
  324.     return(&tx->tx_SavedCPos);
  325. }
  326.  
  327. /*
  328.  * returns the number of buffers saved.
  329.  * (maybe should only save one buffer at a time, then wait to be called
  330.  *  again to save next in line? This could be less intrusive: yes.)
  331.  */
  332. int
  333. auto_save_buffers(void)
  334. {
  335.     /*
  336.      * Stops me entering here more than once at the same time. This
  337.      * can happen when tracing the `auto-save-function'
  338.      */
  339.     static bool Exclusion;
  340.     if(!Exclusion)
  341.     {
  342.     VALUE tx = VAL(buffer_chain);
  343.     u_long time = sys_time();
  344.     Exclusion = TRUE;
  345.     while(tx)
  346.     {
  347.         if(VTX(tx)->tx_Changes
  348.            && VTX(tx)->tx_AutoSaveInterval
  349.            && (VTX(tx)->tx_LastSaveChanges != VTX(tx)->tx_Changes)
  350.            && (time > (VTX(tx)->tx_LastSaveTime + VTX(tx)->tx_AutoSaveInterval)))
  351.         {
  352.         GCVAL gcv_tx;
  353.         PUSHGC(gcv_tx, tx);
  354.         call_lisp1(sym_auto_save_function, tx);
  355.         POPGC;
  356.         VTX(tx)->tx_LastSaveTime = time;
  357.         VTX(tx)->tx_LastSaveChanges = VTX(tx)->tx_Changes;
  358.         Exclusion = FALSE;
  359.         return(1);
  360.         }
  361.         tx = VAL(VTX(tx)->tx_Next);
  362.     }
  363.     Exclusion = FALSE;
  364.     }
  365.     return(0);
  366. }
  367.  
  368. _PR VALUE cmd_current_buffer(VALUE);
  369. DEFUN("current-buffer", cmd_current_buffer, subr_current_buffer, (VALUE vw), V_Subr1, DOC_current_buffer) /*
  370. ::doc:current_buffer::
  371. current-buffer [WINDOW]
  372.  
  373. Return the buffer that WINDOW (or the current window) is displaying.
  374. ::end:: */
  375. {
  376.     if(!WINDOWP(vw))
  377.     vw = VAL(curr_vw);
  378.     return(VAL(VWIN(vw)->vw_Tx));
  379. }
  380.  
  381. _PR VALUE cmd_set_current_buffer(VALUE, VALUE);
  382. DEFUN("set-current-buffer", cmd_set_current_buffer, subr_set_current_buffer, (VALUE tx, VALUE vw), V_Subr2, DOC_set_current_buffer) /*
  383. ::doc:set_current_buffer::
  384. set-current-buffer BUFFER [WINDOW]
  385.  
  386. Set the buffer that WINDOW (or the current window) is displaying. Returns
  387. the buffer which was being displayed before.
  388. ::end:: */
  389. {
  390.     VALUE old;
  391.     DECLARE1(tx, BUFFERP);
  392.     if(!WINDOWP(vw))
  393.     vw = VAL(curr_vw);
  394.     old = VAL(VWIN(vw)->vw_Tx);
  395.     swap_buffers(VWIN(vw), VTX(tx));
  396.     return(old);
  397. }
  398.  
  399. _PR VALUE cmd_buffer_file_name(VALUE);
  400. DEFUN("buffer-file-name", cmd_buffer_file_name, subr_buffer_file_name, (VALUE tx), V_Subr1, DOC_buffer_file_name) /*
  401. ::doc:buffer_file_name::
  402. buffer-file-name [BUFFER]
  403.  
  404. Return the name of the file being edited in BUFFER.
  405. ::end:: */
  406. {
  407.     if(!BUFFERP(tx))
  408.     tx = VAL(curr_vw->vw_Tx);
  409.     return(VTX(tx)->tx_FileName);
  410. }
  411.  
  412. _PR VALUE cmd_set_buffer_file_name(VALUE, VALUE);
  413. DEFUN("set-buffer-file-name", cmd_set_buffer_file_name, subr_set_buffer_file_name, (VALUE tx, VALUE name), V_Subr2, DOC_set_buffer_file_name) /*
  414. ::doc:set_buffer_file_name::
  415. set-buffer-file-name BUFFER NAME
  416.  
  417. Set the name of the file being edited in BUFFER to NAME.
  418. ::end:: */
  419. {
  420.     DECLARE1(name, STRINGP);
  421.     if(!BUFFERP(tx))
  422.     tx = VAL(curr_vw->vw_Tx);
  423.     make_marks_non_resident(VTX(tx));
  424.     VTX(tx)->tx_FileName = name;
  425.     make_marks_resident(VTX(tx));
  426.     return(name);
  427. }
  428.  
  429. _PR VALUE cmd_buffer_name(VALUE);
  430. DEFUN("buffer-name", cmd_buffer_name, subr_buffer_name, (VALUE tx), V_Subr1, DOC_buffer_name) /*
  431. ::doc:buffer_name::
  432. buffer-name [BUFFER]
  433.  
  434. Return the name of BUFFER.
  435. ::end:: */
  436. {
  437.     if(!BUFFERP(tx))
  438.     tx = VAL(curr_vw->vw_Tx);
  439.     return(VTX(tx)->tx_BufferName);
  440. }
  441.  
  442. _PR VALUE cmd_set_buffer_name(VALUE, VALUE);
  443. DEFUN("set-buffer-name", cmd_set_buffer_name, subr_set_buffer_name, (VALUE tx, VALUE name), V_Subr2, DOC_set_buffer_name) /*
  444. ::doc:set_buffer_name::
  445. set-buffer-name BUFFER NAME
  446.  
  447. Set the name of BUFFER to NAME.
  448. ::end:: */
  449. {
  450.     DECLARE1(name, STRINGP);
  451.     if(!BUFFERP(tx))
  452.     tx = VAL(curr_vw->vw_Tx);
  453.     VTX(tx)->tx_BufferName = name;
  454.     sys_reset_sleep_titles(VTX(tx));
  455.     return(name);
  456. }
  457.  
  458. _PR VALUE cmd_buffer_changes(VALUE);
  459. DEFUN("buffer-changes", cmd_buffer_changes, subr_buffer_changes, (VALUE tx), V_Subr1, DOC_buffer_changes) /*
  460. ::doc:buffer_changes::
  461. buffer-changes [BUFFER]
  462.  
  463. Return the number of modifications to BUFFER.
  464. ::end:: */
  465. {
  466.     if(!BUFFERP(tx))
  467.     tx = VAL(curr_vw->vw_Tx);
  468.     return(make_number(VTX(tx)->tx_Changes));
  469. }
  470.  
  471. _PR VALUE cmd_buffer_modified_p(VALUE);
  472. DEFUN("buffer-modified-p", cmd_buffer_modified_p, subr_buffer_modified_p, (VALUE tx), V_Subr1, DOC_buffer_modified_p) /*
  473. ::doc:buffer_modified_p::
  474. buffer-modified-p [BUFFER]
  475.  
  476. Returns t if the buffer has changed since it was last saved.
  477. ::end:: */
  478. {
  479.     if(!BUFFERP(tx))
  480.     tx = VAL(curr_vw->vw_Tx);
  481.     /* TXFF_SPECIAL means this buffer's modifications aren't important. */
  482.     if(VTX(tx)->tx_Flags & TXFF_SPECIAL)
  483.     return(sym_nil);
  484.     if(VTX(tx)->tx_Changes != VTX(tx)->tx_ProperSaveChanges)
  485.     return(sym_t);
  486.     return(sym_nil);
  487. }
  488.  
  489. _PR VALUE cmd_set_buffer_modified(VALUE, VALUE);
  490. DEFUN("set-buffer-modified", cmd_set_buffer_modified, subr_set_buffer_modified, (VALUE tx, VALUE stat), V_Subr2, DOC_set_buffer_modified) /*
  491. ::doc:set_buffer_modified::
  492. set-buffer-modified BUFFER STATUS
  493.  
  494. If STATUS is nil make it look as though buffer hasn't changed, else make
  495. it look as though it has.
  496. ::end:: */
  497. {
  498.     if(!BUFFERP(tx))
  499.     tx = VAL(curr_vw->vw_Tx);
  500.     if(NILP(stat))
  501.     {
  502.     VTX(tx)->tx_ProperSaveChanges = VTX(tx)->tx_Changes;
  503.     VTX(tx)->tx_LastSaveChanges = VTX(tx)->tx_Changes;
  504.     }
  505.     else
  506.     {
  507.     VTX(tx)->tx_ProperSaveChanges = VTX(tx)->tx_Changes - 1;
  508.     VTX(tx)->tx_LastSaveChanges = VTX(tx)->tx_Changes - 1;
  509.     }
  510.     return(tx);
  511. }
  512.  
  513. _PR VALUE cmd_set_buffer_special(VALUE tx, VALUE specialp);
  514. DEFUN("set-buffer-special", cmd_set_buffer_special, subr_set_buffer_special, (VALUE tx, VALUE specialp), V_Subr2, DOC_set_buffer_special) /*
  515. ::doc:set_buffer_special::
  516. set-buffer-special BUFFER SPECIALP
  517.  
  518. When a buffer is `special' it means that it is controlled by some Lisp code,
  519. not by the user typing into it (although this can still happen as well). This
  520. is used for things like the `*jade*' or `*Info*' buffers (in fact most of
  521. the buffers whose names are surrounded by asterisks are special).
  522.  
  523. What the `special' attribute actually does is make sure that the buffer is
  524. never truely killed (`kill-buffer' removes it from each window's `buffer-list'
  525. but doesn't detroy the actual contents) and modifications don't cause the
  526. `+' marker to appear in the status line.
  527. ::end:: */
  528. {
  529.     if(!BUFFERP(tx))
  530.     tx = VAL(curr_vw->vw_Tx);
  531.     if(NILP(specialp))
  532.     VTX(tx)->tx_Flags &= ~TXFF_SPECIAL;
  533.     else
  534.     VTX(tx)->tx_Flags |= TXFF_SPECIAL;
  535.     return(tx);
  536. }
  537.  
  538. _PR VALUE cmd_buffer_special_p(VALUE tx);
  539. DEFUN("buffer-special-p", cmd_buffer_special_p, subr_buffer_special_p, (VALUE tx), V_Subr1, DOC_buffer_special_p) /*
  540. ::doc:buffer_special_p::
  541. buffer-special-p [BUFFER]
  542.  
  543. Returns t if BUFFER is ``special''. See `set-buffer-special' for the meaning of
  544. the ``special'' attribute.
  545. ::end:: */
  546. {
  547.     if(!BUFFERP(tx))
  548.     tx = VAL(curr_vw->vw_Tx);
  549.     if(VTX(tx)->tx_Flags & TXFF_SPECIAL)
  550.     return(sym_t);
  551.     return(sym_nil);
  552. }
  553.  
  554. _PR VALUE cmd_set_buffer_read_only(VALUE tx, VALUE stat);
  555. DEFUN("set-buffer-read-only", cmd_set_buffer_read_only, subr_set_buffer_read_only, (VALUE tx, VALUE stat), V_Subr2, DOC_set_buffer_read_only) /*
  556. ::doc:set_buffer_read_only::
  557. set-buffer-read-only BUFFER READ-ONLY-P
  558.  
  559. If a buffer is read-only no modification of its contents is allowed.
  560. ::end:: */
  561. {
  562.     if(!BUFFERP(tx))
  563.     tx = VAL(curr_vw->vw_Tx);
  564.     if(NILP(stat))
  565.     VTX(tx)->tx_Flags &= ~TXFF_RDONLY;
  566.     else
  567.     VTX(tx)->tx_Flags |= TXFF_RDONLY;
  568.     return(tx);
  569. }
  570.  
  571. _PR VALUE cmd_buffer_read_only_p(VALUE tx);
  572. DEFUN("buffer-read-only-p", cmd_buffer_read_only_p, subr_buffer_read_only_p, (VALUE tx), V_Subr1, DOC_buffer_read_only_p) /*
  573. ::doc:buffer_read_only_p::
  574. buffer-read-only-p [BUFFER]
  575.  
  576. Returns t if BUFFER is read-only. See `set-buffer-read-only'.
  577. ::end:: */
  578. {
  579.     if(!BUFFERP(tx))
  580.     tx = VAL(curr_vw->vw_Tx);
  581.     if(VTX(tx)->tx_Flags & TXFF_RDONLY)
  582.     return(sym_t);
  583.     return(sym_nil);
  584. }
  585.  
  586. _PR VALUE cmd_buffer_length(VALUE);
  587. DEFUN("buffer-length", cmd_buffer_length, subr_buffer_length, (VALUE tx), V_Subr1, DOC_buffer_length) /*
  588. ::doc:buffer_length::
  589. buffer-length [BUFFER]
  590.  
  591. Returns the number of lines in BUFFER.
  592. ::end:: */
  593. {
  594.     if(!BUFFERP(tx))
  595.     tx = VAL(curr_vw->vw_Tx);
  596.     return(make_number(VTX(tx)->tx_NumLines));
  597. }
  598.  
  599. _PR VALUE cmd_line_length(VALUE, VALUE);
  600. DEFUN("line-length", cmd_line_length, subr_line_length, (VALUE pos, VALUE tx), V_Subr2, DOC_line_length) /*
  601. ::doc:line_length::
  602. line-length [LINE-POS] [BUFFER]
  603.  
  604. Returns the length (not including newline) of the specified line, or
  605. using current cursor position if specifiers are not provided.
  606. ::end:: */
  607. {
  608.     POS p;
  609.     if(POSP(pos))
  610.     {
  611.     if(!BUFFERP(tx))
  612.         tx = VAL(curr_vw->vw_Tx);
  613.     p = VPOS(pos);
  614.     }
  615.     else
  616.     {
  617.     p = curr_vw->vw_CursorPos;
  618.     tx = VAL(curr_vw->vw_Tx);
  619.     }
  620.     return(make_number(VTX(tx)->tx_Lines[p.pos_Line].ln_Strlen - 1));
  621. }
  622.  
  623. _PR VALUE cmd_with_buffer(VALUE);
  624. DEFUN("with-buffer", cmd_with_buffer, subr_with_buffer, (VALUE args), V_SF, DOC_with_buffer) /*
  625. ::doc:with_buffer::
  626. with-buffer BUFFER FORMS...
  627.  
  628. Temporarily switches to buffer, then executes the FORMS in it before
  629. returning to the original buffer.
  630. ::end:: */
  631. {
  632.     if(CONSP(args))
  633.     {
  634.     GCVAL gcv_args;
  635.     VALUE res;
  636.     PUSHGC(gcv_args, args);
  637.     if((res = cmd_eval(VCAR(args))) && BUFFERP(res))
  638.     {
  639.         VALUE oldtx = VAL(swap_buffers_tmp(curr_vw, VTX(res)));
  640.         if(oldtx)
  641.         {
  642.         GCVAL gcv_oldtx;
  643.         PUSHGC(gcv_oldtx, oldtx);
  644.         res = cmd_progn(VCDR(args));
  645.         POPGC;
  646.         swap_buffers_tmp(curr_vw, VTX(oldtx));
  647.         }
  648.     }
  649.     POPGC;
  650.     return(res);
  651.     }
  652.     return(NULL);
  653. }
  654.  
  655. _PR VALUE cmd_bufferp(VALUE);
  656. DEFUN("bufferp", cmd_bufferp, subr_bufferp, (VALUE arg), V_Subr1, DOC_bufferp) /*
  657. ::doc:bufferp::
  658. bufferp ARG
  659.  
  660. Returns t if ARG is a buffer.
  661. ::end:: */
  662. {
  663.     if(BUFFERP(arg))
  664.     return(sym_t);
  665.     return(sym_nil);
  666. }
  667.  
  668. _PR VALUE var_auto_save_interval(VALUE);
  669. DEFUN("auto-save-interval", var_auto_save_interval, subr_auto_save_interval, (VALUE val), V_Var, DOC_auto_save_interval) /*
  670. ::doc:auto_save_interval::
  671. The number of modifications which must be made to a buffer before it
  672. is considered for auto-saving. A value of zero means that this buffer
  673. is not to be auto-saved.
  674. ::end:: */
  675. {
  676.     return(handle_var_int(val, &curr_vw->vw_Tx->tx_AutoSaveInterval));
  677. }
  678.  
  679. _PR VALUE var_last_save_changes(VALUE);
  680. DEFUN("last-save-changes", var_last_save_changes, subr_last_save_changes, (VALUE val), V_Var, DOC_last_save_changes) /*
  681. ::doc:last_save_changes::
  682. Number of changes the last time this buffer was saved (could be auto-save).
  683. ::end:: */
  684. {
  685.     return(handle_var_int(val, &curr_vw->vw_Tx->tx_LastSaveChanges));
  686. }
  687.  
  688. _PR VALUE var_last_user_save_changes(VALUE);
  689. DEFUN("last-user-save-changes", var_last_user_save_changes, subr_last_user_save_changes, (VALUE val), V_Var, DOC_last_user_save_changes) /*
  690. ::doc:last_user_save_changes::
  691. Number of changes the last time this buffer was saved (not from auto-save).
  692. ::end:: */
  693. {
  694.     return(handle_var_int(val, &curr_vw->vw_Tx->tx_ProperSaveChanges));
  695. }
  696.  
  697. _PR VALUE var_last_save_time(VALUE);
  698. DEFUN("last-save-time", var_last_save_time, subr_last_save_time, (VALUE val), V_Var, DOC_last_save_time) /*
  699. ::doc:last_save_time::
  700. System time at last save of this buffer (could be from an auto-save).
  701. ::end:: */
  702. {
  703.     return(handle_var_int(val, &curr_vw->vw_Tx->tx_LastSaveTime));
  704. }
  705.  
  706. _PR VALUE var_tab_size(VALUE);
  707. DEFUN("tab-size", var_tab_size, subr_tab_size, (VALUE val), V_Var, DOC_tab_size) /*
  708. ::doc:tab_size::
  709. Sets the size of tab-stops.
  710. ::end:: */
  711. {
  712.     return(handle_var_int(val, &curr_vw->vw_Tx->tx_TabSize));
  713. }
  714.  
  715. _PR VALUE var_mode_name(VALUE);
  716. DEFUN("mode-name", var_mode_name, subr_mode_name, (VALUE val), V_Var, DOC_mode_name) /*
  717. ::doc:mode_name::
  718. This is used to display the name of the edit-mode being used in the status
  719. line.
  720. ::end:: */
  721. {
  722.     TX *tx = curr_vw->vw_Tx;
  723.     if(val)
  724.     {
  725.     if(STRINGP(val))
  726.         tx->tx_ModeName = val;
  727.     else
  728.         tx->tx_ModeName = NULL;
  729.     return(val);
  730.     }
  731.     else if(tx->tx_ModeName)
  732.     return(tx->tx_ModeName);
  733.     return(sym_nil);
  734. }
  735.  
  736. _PR VALUE var_minor_mode_names(VALUE);
  737. DEFUN("minor-mode-names", var_minor_mode_names, subr_minor_mode_names, (VALUE val), V_Var, DOC_minor_mode_names) /*
  738. ::doc:minor_mode_names::
  739. List of strings naming all minor-modes enabled in this buffer.
  740. ::end:: */
  741. {
  742.     TX *tx = curr_vw->vw_Tx;
  743.     if(!val)
  744.     return(tx->tx_MinorModeNameList);
  745.     if(!CONSP(val))
  746.     val = sym_nil;
  747.     tx->tx_MinorModeNameList = val;
  748.     if(NILP(val))
  749.     tx->tx_MinorModeNameString = null_string;
  750.     else
  751.     {
  752.     int len;
  753.     u_char *str;
  754.     VALUE tmp = val;
  755.     for(len = 0; CONSP(tmp) && STRINGP(VCAR(tmp)); tmp = VCDR(tmp))
  756.         len += STRING_LEN(VCAR(tmp)) + 1;
  757.     tx->tx_MinorModeNameString = make_string(len + 1);
  758.     str = VSTR(tx->tx_MinorModeNameString);
  759.     tmp = val;
  760.     while(CONSP(tmp) && STRINGP(VCAR(tmp)))
  761.     {
  762.         *str++ = ' ';
  763.         str = stpcpy(str, VSTR(VCAR(tmp)));
  764.         tmp = VCDR(tmp);
  765.     }
  766.     }
  767.     return(val);
  768. }
  769.  
  770. /* chain of all non-resident marks, linked via `mk_Next' */
  771. static Mark *non_resident_mark_chain;
  772. /* chain of all marks, linked via `mk_NextAlloc' */
  773. static Mark *mark_chain;
  774.  
  775. /*
  776.  * For all non-resident marks, see if any point to NEWTX, if so link them
  777.  * onto NEWTX's `tx_Marks' chain.
  778.  */
  779. void
  780. make_marks_resident(TX *newtx)
  781. {
  782.     Mark *nxt, *mk = non_resident_mark_chain;
  783.     non_resident_mark_chain = NULL;
  784.     while(mk)
  785.     {
  786.     nxt = mk->mk_Next;
  787.     if(same_files(VSTR(newtx->tx_FileName), VSTR(mk->mk_File.name)))
  788.     {
  789.         mk->mk_File.tx = newtx;
  790.         mk->mk_Resident = TRUE;
  791.         mk->mk_Next = newtx->tx_MarkChain;
  792.         newtx->tx_MarkChain = mk;
  793.     }
  794.     else
  795.     {
  796.         mk->mk_Next = non_resident_mark_chain;
  797.         non_resident_mark_chain = mk;
  798.     }
  799.     mk = nxt;
  800.     }
  801. }
  802.  
  803. void
  804. make_marks_non_resident(TX *oldtx)
  805. {
  806.     Mark *nxt, *mk = oldtx->tx_MarkChain;
  807.     oldtx->tx_MarkChain = NULL;
  808.     while(mk)
  809.     {
  810.     nxt = mk->mk_Next;
  811.     mk->mk_File.name = oldtx->tx_FileName;
  812.     mk->mk_Resident = FALSE;
  813.     mk->mk_Next = non_resident_mark_chain;
  814.     non_resident_mark_chain = mk;
  815.     mk = nxt;
  816.     }
  817. }
  818.  
  819. /*
  820.  * Takes MK off the buffer mark chain that it's on (or the non_resident_mark_chain).
  821.  */
  822. static void
  823. unchain_mark(Mark *mk)
  824. {
  825.     Mark **headp, *this;
  826.     if(!mk->mk_Resident)
  827.     headp = &non_resident_mark_chain;
  828.     else
  829.     headp = &(mk->mk_File.tx->tx_MarkChain);
  830.     this = *headp;
  831.     *headp = NULL;
  832.     while(this)
  833.     {
  834.     Mark *tmp = this->mk_Next;
  835.     if(this != mk)
  836.     {
  837.         this->mk_Next = *headp;
  838.         *headp = this;
  839.     }
  840.     this = tmp;
  841.     }
  842. }
  843.  
  844. void
  845. mark_sweep(void)
  846. {
  847.     Mark *mk = mark_chain;
  848.     mark_chain = NULL;
  849.     while(mk)
  850.     {
  851.     Mark *nxt = mk->mk_NextAlloc;
  852.     if(!GC_MARKEDP(VAL(mk)))
  853.     {
  854.         unchain_mark(mk);
  855.         str_free(mk);
  856.     }
  857.     else
  858.     {
  859.         GC_CLR(VAL(mk));
  860.         mk->mk_NextAlloc = mark_chain;
  861.         mark_chain = mk;
  862.     }
  863.     mk = nxt;
  864.     }
  865. }
  866.  
  867. int
  868. mark_cmp(VALUE v1, VALUE v2)
  869. {
  870.     int rc = 1;
  871.     if(VTYPE(v1) == VTYPE(v2))
  872.     {
  873.     u_char *name1, *name2;
  874.     if(VMARK(v1)->mk_Resident)
  875.         name1 = VSTR(VMARK(v1)->mk_File.tx->tx_FileName);
  876.     else
  877.         name1 = VSTR(VMARK(v1)->mk_File.name);
  878.     if(VMARK(v2)->mk_Resident)
  879.         name2 = VSTR(VMARK(v2)->mk_File.tx->tx_FileName);
  880.     else
  881.         name2 = VSTR(VMARK(v2)->mk_File.name);
  882.     if(same_files(name1, name2))
  883.     {
  884.         if(!(rc = VPOS(VMARK(v1)->mk_Pos).pos_Line - VPOS(VMARK(v2)->mk_Pos).pos_Line))
  885.         rc = VPOS(VMARK(v1)->mk_Pos).pos_Col - VPOS(VMARK(v2)->mk_Pos).pos_Col;
  886.     }
  887.     }
  888.     return(rc);
  889. }
  890.  
  891. void
  892. mark_prin(VALUE strm, VALUE obj)
  893. {
  894.     u_char tbuf[40];
  895.     stream_puts(strm, "#<mark ", -1, FALSE);
  896.     if(VMARK(obj)->mk_Resident)
  897.     buffer_prin(strm, VAL(VMARK(obj)->mk_File.tx));
  898.     else
  899.     {
  900.     stream_putc(strm, '"');
  901.     stream_puts(strm, VSTR(VMARK(obj)->mk_File.name), -1, TRUE);
  902.     stream_putc(strm, '"');
  903.     }
  904.     sprintf(tbuf, " #<pos %ld %ld>>",
  905.         VPOS(VMARK(obj)->mk_Pos).pos_Col,
  906.         VPOS(VMARK(obj)->mk_Pos).pos_Line);
  907.     stream_puts(strm, tbuf, -1, FALSE);
  908. }
  909.  
  910. _PR VALUE cmd_make_mark(VALUE, VALUE);
  911. DEFUN("make-mark", cmd_make_mark, subr_make_mark, (VALUE pos, VALUE buffer), V_Subr2, DOC_make_mark) /*
  912. ::doc:make_mark::
  913. make-mark [POS] [BUFFER | FILE-NAME]
  914.  
  915. Creates a new mark pointing to position POS either in the current file
  916. or in FILE-NAME, or BUFFER.
  917.  
  918. Note that FILE-NAME doesn't have to be a file that has been loaded, it's
  919. stored as a string, the file it points to is only opened when needed.
  920.  
  921. Unlike position objects, the position in a file that a mark points to is
  922. updated as the file changes -- it will always point to the same character
  923. (for as long as that character exists, anyway).
  924. ::end:: */
  925. {
  926.     Mark *mk = NULL;
  927.     if((mk = str_alloc(sizeof(Mark))) && (mk->mk_Pos = make_lpos(NULL)))
  928.     {
  929.     mk->mk_Type = V_Mark;
  930.     mk->mk_NextAlloc = mark_chain;
  931.     mark_chain = mk;
  932.     data_after_gc += sizeof(Mark);
  933.     if(POSP(pos))
  934.         VPOS(mk->mk_Pos) = VPOS(pos);
  935.     else
  936.         VPOS(mk->mk_Pos) = curr_vw->vw_CursorPos;
  937.     if(STRINGP(buffer))
  938.     {
  939.         VALUE tx;
  940.         if((tx = cmd_get_file_buffer(buffer)) && BUFFERP(tx))
  941.         {
  942.         mk->mk_Resident = TRUE;
  943.         mk->mk_File.tx = VTX(tx);
  944.         mk->mk_Next = VTX(tx)->tx_MarkChain;
  945.         VTX(tx)->tx_MarkChain = mk;
  946.         }
  947.         else
  948.         {
  949.         mk->mk_Resident = FALSE;
  950.         mk->mk_File.name = buffer;
  951.         mk->mk_Next = non_resident_mark_chain;
  952.         non_resident_mark_chain = mk;
  953.         }
  954.     }
  955.     else
  956.     {
  957.         if(!BUFFERP(buffer))
  958.         buffer = VAL(curr_vw->vw_Tx);
  959.         mk->mk_Resident = TRUE;
  960.         mk->mk_File.tx = VTX(buffer);
  961.         mk->mk_Next = VTX(buffer)->tx_MarkChain;
  962.         VTX(buffer)->tx_MarkChain = mk;
  963.     }
  964.     return(VAL(mk));
  965.     }
  966.     if(mk)
  967.     str_free(mk);
  968.     return(NULL);
  969. }
  970.  
  971. _PR VALUE cmd_set_mark(VALUE, VALUE, VALUE);
  972. DEFUN("set-mark", cmd_set_mark, subr_set_mark, (VALUE mark, VALUE pos, VALUE buffer), V_Subr3, DOC_set_mark) /*
  973. ::doc:set_mark::
  974. set-mark MARK [POS] [FILE-NAME | BUFFER]
  975.  
  976. Sets the position which MARK points to POS in FILE-NAME or BUFFER.
  977. ::end:: */
  978. {
  979.     DECLARE1(mark, MARKP);
  980.     if(POSP(pos))
  981.     VPOS(VMARK(mark)->mk_Pos) = VPOS(pos);
  982.     if(BUFFERP(buffer) || STRINGP(buffer))
  983.     {
  984.     Mark *mk, **chain;
  985.     if(VMARK(mark)->mk_Resident)
  986.         chain = &(VMARK(mark)->mk_File.tx->tx_MarkChain);
  987.     else
  988.         chain = &non_resident_mark_chain;
  989.     mk = *chain;
  990.     *chain = NULL;
  991.     while(mk)
  992.     {
  993.         Mark *nxt = mk->mk_Next;
  994.         if(mk != VMARK(mark))
  995.         {
  996.         mk->mk_Next = *chain;
  997.         *chain = mk;
  998.         }
  999.         else
  1000.         mk->mk_Next = NULL;
  1001.         mk = nxt;
  1002.     }
  1003.     VMARK(mark)->mk_File.name = buffer;
  1004.     switch(VTYPE(buffer))
  1005.     {
  1006.         VALUE tmp;
  1007.     case V_StaticString:
  1008.     case V_DynamicString:
  1009.         tmp = cmd_get_file_buffer(buffer);
  1010.         if((tmp == NULL) || NILP(tmp))
  1011.         {
  1012.         VMARK(mark)->mk_Resident = FALSE;
  1013.         VMARK(mark)->mk_Next = non_resident_mark_chain;
  1014.         non_resident_mark_chain = VMARK(mark);
  1015.         break;
  1016.         }
  1017.         VMARK(mark)->mk_File.name = tmp;
  1018.         /* FALL THROUGH */
  1019.     case V_TX:
  1020.         VMARK(mark)->mk_Resident = TRUE;
  1021.         VMARK(mark)->mk_Next = VMARK(mark)->mk_File.tx->tx_MarkChain;
  1022.         VMARK(mark)->mk_File.tx->tx_MarkChain = VMARK(mark);
  1023.         break;
  1024.     }
  1025.     }
  1026.     return(mark);
  1027. }
  1028.  
  1029. _PR VALUE cmd_mark_pos(VALUE);
  1030. DEFUN("mark-pos", cmd_mark_pos, subr_mark_pos, (VALUE mark), V_Subr1, DOC_mark_pos) /*
  1031. ::doc:mark_pos::
  1032. mark-pos MARK
  1033.  
  1034. Returns the position that MARK points to. (note that this is the *same*
  1035. object that the mark stores internally -- so don't modify it unless you're
  1036. really sure you know what you're doing)
  1037. ::end:: */
  1038. {
  1039.     DECLARE1(mark, MARKP);
  1040.     return(VMARK(mark)->mk_Pos);
  1041. }
  1042.  
  1043. _PR VALUE cmd_mark_file(VALUE);
  1044. DEFUN("mark-file", cmd_mark_file, subr_mark_file, (VALUE mark), V_Subr1, DOC_mark_file) /*
  1045. ::doc:mark_file::
  1046. mark-file MARK
  1047.  
  1048. Returns the file-name or buffer that MARK points to.
  1049. ::end:: */
  1050. {
  1051.     DECLARE1(mark, MARKP);
  1052.     return(VMARK(mark)->mk_File.name);
  1053. }
  1054.  
  1055. _PR VALUE cmd_mark_resident_p(VALUE);
  1056. DEFUN("mark-resident-p", cmd_mark_resident_p, subr_mark_resident_p, (VALUE mark), V_Subr1, DOC_mark_resident_p) /*
  1057. ::doc:mark_resident_p::
  1058. mark-resident-p MARK
  1059.  
  1060. Returns t if the file that MARK points to is in a buffer.
  1061. ::end:: */
  1062. {
  1063.     DECLARE1(mark, MARKP);
  1064.     if(VMARK(mark)->mk_Resident)
  1065.     return(sym_t);
  1066.     return(sym_nil);
  1067. }
  1068.  
  1069. _PR VALUE cmd_markp(VALUE);
  1070. DEFUN("markp", cmd_markp, subr_markp, (VALUE mark), V_Subr1, DOC_markp) /*
  1071. ::doc:markp::
  1072. markp ARG
  1073.  
  1074. Return t if ARG is a mark.
  1075. ::end:: */
  1076. {
  1077.     if(MARKP(mark))
  1078.     return(sym_t);
  1079.     return(sym_nil);
  1080. }
  1081.  
  1082. void
  1083. buffers_init(void)
  1084. {
  1085.     mark_static((VALUE *)&non_resident_mark_chain);
  1086.     INTERN(sym_auto_save_function, "auto-save-function");
  1087.     INTERN(sym_leading, "leading");
  1088.     INTERN(sym_all, "all");
  1089.     ADD_SUBR(subr_make_buffer_name);
  1090.     ADD_SUBR(subr_make_buffer);
  1091.     ADD_SUBR(subr_destroy_buffer);
  1092.     ADD_SUBR(subr_get_file_buffer);
  1093.     ADD_SUBR(subr_get_buffer);
  1094.     ADD_SUBR(subr_current_buffer);
  1095.     ADD_SUBR(subr_set_current_buffer);
  1096.     ADD_SUBR(subr_buffer_file_name);
  1097.     ADD_SUBR(subr_set_buffer_file_name);
  1098.     ADD_SUBR(subr_buffer_name);
  1099.     ADD_SUBR(subr_set_buffer_name);
  1100.     ADD_SUBR(subr_buffer_changes);
  1101.     ADD_SUBR(subr_buffer_modified_p);
  1102.     ADD_SUBR(subr_set_buffer_modified);
  1103.     ADD_SUBR(subr_set_buffer_special);
  1104.     ADD_SUBR(subr_buffer_special_p);
  1105.     ADD_SUBR(subr_set_buffer_read_only);
  1106.     ADD_SUBR(subr_buffer_read_only_p);
  1107.     ADD_SUBR(subr_buffer_length);
  1108.     ADD_SUBR(subr_line_length);
  1109.     ADD_SUBR(subr_with_buffer);
  1110.     ADD_SUBR(subr_bufferp);
  1111.     ADD_SUBR(subr_auto_save_interval);
  1112.     ADD_SUBR(subr_last_save_changes);
  1113.     ADD_SUBR(subr_last_user_save_changes);
  1114.     ADD_SUBR(subr_last_save_time);
  1115.     ADD_SUBR(subr_tab_size);
  1116.     ADD_SUBR(subr_mode_name);
  1117.     ADD_SUBR(subr_minor_mode_names);
  1118.     ADD_SUBR(subr_make_mark);
  1119.     ADD_SUBR(subr_set_mark);
  1120.     ADD_SUBR(subr_mark_pos);
  1121.     ADD_SUBR(subr_mark_file);
  1122.     ADD_SUBR(subr_mark_resident_p);
  1123.     ADD_SUBR(subr_markp);
  1124. }
  1125.  
  1126. void
  1127. buffers_kill(void)
  1128. {
  1129.     TX *tx = buffer_chain;
  1130.     Mark *mk = mark_chain;
  1131.     while(tx)
  1132.     {
  1133.     TX *nxttx = tx->tx_Next;
  1134.     kill_line_list(tx);
  1135.     myfree(tx);
  1136.     tx = nxttx;
  1137.     }
  1138.     buffer_chain = NULL;
  1139.     while(mk)
  1140.     {
  1141.     Mark *nxtmk = mk->mk_NextAlloc;
  1142.     str_free(mk);
  1143.     mk = nxtmk;
  1144.     }
  1145.     mark_chain = NULL;
  1146. }
  1147.